package util

import (
	"bytes"
	"fmt"
)

type Int32List []int32

// implement sort interface
func (self Int32List) Len() int {
	return len(self)
}
func (self Int32List) Less(i, j int) bool {
	return self[i] < self[j]
}
func (self Int32List) Swap(i, j int) {
	self[i], self[j] = self[j], self[i]
}
func (self Int32List) GetLen() int32 {
	return int32(len(self))
}
func (self Int32List) GetInt(idx int) int32 {
	return self[idx]
}

type Int32Seq []int32

// implement IntList interface
func (self Int32Seq) GetLen() int32 {
	return int32(len(self))
}
func (self Int32Seq) GetInt(idx int) int32 {
	return self[idx]
}
func (self Int32Seq) IsReverse() bool {
	return false
}
func (self Int32Seq) GetMinInt() int32 {
	if len(self) <= 0 {
		return 0
	}
	return self[0]
}

type Int32ReverseSeq []int32

// implement IntList interface
func (self Int32ReverseSeq) GetLen() int32 {
	return int32(len(self))
}
func (self Int32ReverseSeq) GetInt(idx int) int32 {
	return self[idx]
}
func (self Int32ReverseSeq) IsReverse() bool {
	return true
}
func (self Int32ReverseSeq) GetMinInt() int32 {
	if len(self) <= 0 {
		return 0
	}
	return self[len(self)-1]
}

type UInt16List []uint16

func (self UInt16List) GetLen() int32 {
	return int32(len(self))
}
func (self UInt16List) GetInt(idx int) int32 {
	return int32(self[idx])
}

type UInt16Seq []uint16

// implement IntList interface
func (self UInt16Seq) GetLen() int32 {
	return int32(len(self))
}
func (self UInt16Seq) GetInt(idx int) int32 {
	return int32(self[idx])
}
func (self UInt16Seq) IsReverse() bool {
	return false
}
func (self UInt16Seq) GetMinInt() int32 {
	if len(self) <= 0 {
		return 0
	}
	return int32(self[0])
}

type UInt8Seq []uint8

// implement IntList interface
func (self UInt8Seq) GetLen() int32 {
	return int32(len(self))
}
func (self UInt8Seq) GetInt(idx int) int32 {
	return int32(self[idx])
}
func (self UInt8Seq) IsReverse() bool {
	return false
}
func (self UInt8Seq) GetMinInt() int32 {
	if len(self) <= 0 {
		return 0
	}
	return int32(self[0])
}

type IntList interface {
	GetLen() int32
	GetInt(idx int) int32
}

type IntSeq interface {
	IntList
	IsReverse() bool
	GetMinInt() int32
}

type IntKeyValList interface {
	GetLen() int32
	GetVal(idx int, key string) int32
}

type IntKeyValsList interface {
	IntList
	GetVals(idx int, key string) []int32
}

func CheckIntSeqList(seq IntSeq) error {
	slen := int(seq.GetLen())
	if slen <= 0 {
		return fmt.Errorf("seq len should not be 0")
	}

	reverse := seq.IsReverse()

	if reverse {
		if seq.GetInt(slen-1) != seq.GetMinInt() {
			return fmt.Errorf("seq last int should be %d", seq.GetMinInt())
		}
	} else {
		if seq.GetInt(0) != seq.GetMinInt() {
			return fmt.Errorf("seq first int should be %d", seq.GetMinInt())
		}
	}

	lastVal := seq.GetInt(0)
	for i := 1; i < slen; i++ {
		val := seq.GetInt(i)
		ok := false
		if reverse {
			ok = val == lastVal-1
		} else {
			ok = val == lastVal+1
		}
		if !ok {
			return fmt.Errorf("seq(reverse:%t) is broken, idx:%d, val:%d, is not continous", reverse, i, val)
		}
		lastVal = val
	}
	return nil
}

func CheckIntUncontinuousSeqList(seq IntSeq) error {
	slen := int(seq.GetLen())
	if slen <= 0 {
		return fmt.Errorf("seq len should not be 0")
	}

	reverse := seq.IsReverse()

	if reverse {
		if seq.GetInt(slen-1) != seq.GetMinInt() {
			return fmt.Errorf("seq last int should be %d", seq.GetMinInt())
		}
	} else {
		if seq.GetInt(0) != seq.GetMinInt() {
			return fmt.Errorf("seq first int should be %d", seq.GetMinInt())
		}
	}

	lastVal := seq.GetInt(0)
	for i := 1; i < slen; i++ {
		val := seq.GetInt(i)
		ok := false
		if reverse {
			ok = val < lastVal
		} else {
			ok = val > lastVal
		}
		if !ok {
			return fmt.Errorf("seq(reverse:%t) is broken, idx:%d, val:%d, is wrong order", reverse, i, val)
		}
		lastVal = val
	}
	return nil
}

func CheckIntListDuplicate(ints IntList) (err error) {
	slen := int(ints.GetLen())
	if slen <= 0 {
		return fmt.Errorf("ints len should not be 0")
	}

	var errInfo *bytes.Buffer

	m := map[int32]byte{}
	for i := 0; i < slen; i++ {
		v := ints.GetInt(i)
		_, existed := m[v]
		if existed {
			if errInfo == nil {
				errInfo = &bytes.Buffer{}
			}
			errInfo.WriteString(fmt.Sprintf("\tdup int found: %d\n", v))
		} else {
			m[v] = 1
		}
	}

	if errInfo == nil {
		return nil
	} else {
		return fmt.Errorf(errInfo.String())
	}
}

func CheckIntSameDirectionList(ints IntKeyValList, key string, reverse bool, isEqualOk bool) error {
	slen := int(ints.GetLen())
	if slen <= 0 {
		return fmt.Errorf("ints len should not be 0")
	}

	lastVal := ints.GetVal(0, key)
	for i := 1; i < slen; i++ {
		val := ints.GetVal(i, key)
		ok := false
		if reverse {
			if isEqualOk {
				ok = val <= lastVal
			} else {
				ok = val < lastVal
			}
		} else {
			if isEqualOk {
				ok = val >= lastVal
			} else {
				ok = val > lastVal
			}
		}
		if !ok {
			return fmt.Errorf("same direction ints key(%s, reverse:%t) is broken, idx:%d, val:%d, is wrong order", key, reverse, i, val)
		}
		lastVal = val
	}
	return nil
}

func CheckIntSameDirectionListAry(ints IntKeyValsList, key string, reverse bool, isEqualOk bool) error {
	slen := int(ints.GetLen())
	if slen <= 0 {
		return fmt.Errorf("ints len should not be 0")
	}

	var errInfo *bytes.Buffer

	for i := 0; i < slen; i++ {
		vals := ints.GetVals(i, key)

		lastVal := vals[0]
		for k := 1; k < len(vals); k++ {
			val := vals[k]
			ok := false
			if reverse {
				if isEqualOk {
					ok = val <= lastVal
				} else {
					ok = val < lastVal
				}
			} else {
				if isEqualOk {
					ok = val >= lastVal
				} else {
					ok = val > lastVal
				}
			}
			if !ok {
				if errInfo == nil {
					errInfo = &bytes.Buffer{}
				}
				errInfo.WriteString(fmt.Sprintf("same direction ints aryKey(%s, reverse:%t) is broken, row:%d, idx:%d, val:%d, is wrong order\n", key, reverse, i, k, val))
			}
			lastVal = val
		}
	}

	if errInfo == nil {
		return nil
	} else {
		return fmt.Errorf(errInfo.String())
	}
}

func CheckIntSameDirectionListAlignAry(ints IntKeyValsList, key string, reverse bool, isEqualOk bool) error {
	slen := int(ints.GetLen())
	if slen <= 0 {
		return fmt.Errorf("ints len should not be 0")
	}

	aryLen := len(ints.GetVals(0, key))

	lastVals := ints.GetVals(0, key)
	for i := 1; i < slen; i++ {
		vals := ints.GetVals(i, key)
		if len(vals) != aryLen {
			return fmt.Errorf("aryKey:%s, row:%d len(%d) is not the same as other row(len:%d)", key, i, len(vals), aryLen)
		}

		for k := 0; k < len(vals); k++ {
			ok := false
			if reverse {
				if isEqualOk {
					ok = vals[k] <= lastVals[k]
				} else {
					ok = vals[k] < lastVals[k]
				}
			} else {
				if isEqualOk {
					ok = vals[k] >= lastVals[k]
				} else {
					ok = vals[k] > lastVals[k]
				}
			}
			if !ok {
				return fmt.Errorf("same direction align ints key(%s, reverse:%t) is broken, idx:%d, aryIdx:%d, vals:%v, is wrong order", key, reverse, i, k, vals)
			}
		}
		lastVals = vals
	}
	return nil
}

func CheckIntListRangeAry(ints IntKeyValsList, key string, min int32, max int32) error {
	slen := int(ints.GetLen())
	if slen <= 0 {
		return fmt.Errorf("ints len should not be 0")
	}

	var errInfo *bytes.Buffer

	for i := 0; i < slen; i++ {
		vals := ints.GetVals(i, key)
		for k := 0; k < len(vals); k++ {
			v := vals[k]
			if v < min || max < v {
				if errInfo == nil {
					errInfo = &bytes.Buffer{}
				}
				errInfo.WriteString(fmt.Sprintf("key:%s, row:%d, idx:%d, val:%d is out of range [%d, %d]\n", key, i, k, v, min, max))
			}
		}
	}

	if errInfo == nil {
		return nil
	} else {
		return fmt.Errorf(errInfo.String())
	}
}

func CheckIntListNotAllZeroAndAllNotNegativeAry(ints IntKeyValsList, key string) error {
	slen := int(ints.GetLen())
	if slen <= 0 {
		return fmt.Errorf("ints len should not be 0")
	}

	var errInfo *bytes.Buffer

	for i := 0; i < slen; i++ {
		vals := ints.GetVals(i, key)
		zeroCnt := 0
		for k := 0; k < len(vals); k++ {
			if vals[k] == 0 {
				zeroCnt++
			} else if vals[k] < 0 {
				return fmt.Errorf("key:%s, row:%d, idx:%d, should be >= 0", key, i, k)
			}
		}

		if zeroCnt == len(vals) {
			if errInfo == nil {
				errInfo = &bytes.Buffer{}
			}
			errInfo.WriteString(fmt.Sprintf("key:%s, row:%d, should be not all 0\n", key, i))
		}
	}

	if errInfo == nil {
		return nil
	} else {
		return fmt.Errorf(errInfo.String())
	}
}

func CheckIntListNotAllZeroAndAllNotNegativeAlignAry(ints IntKeyValsList, key string) error {
	slen := int(ints.GetLen())
	if slen <= 0 {
		return fmt.Errorf("ints len should not be 0")
	}

	aryLen := len(ints.GetVals(0, key))
	zeroCnts := make([]int, aryLen)
	for i := 0; i < slen; i++ {
		vals := ints.GetVals(i, key)
		if len(vals) != aryLen {
			return fmt.Errorf("aryKey:%s, row:%d len(%d) is not the same as other row(len:%d)", key, i, len(vals), aryLen)
		}

		for k := 0; k < len(vals); k++ {
			if vals[k] == 0 {
				zeroCnts[k]++
			} else if vals[k] < 0 {
				return fmt.Errorf("key:%s, row:%d, idx:%d, should be >= 0", key, i, k)
			}
		}
	}

	for i := 0; i < len(zeroCnts); i++ {
		if zeroCnts[i] == slen {
			return fmt.Errorf("aryKey:%s, idx:%d, should not be all zero", key, i)
		}
	}
	return nil
}

func CheckIntListRange(ints IntKeyValList, key string, min int32, max int32) (err error) {
	slen := int(ints.GetLen())
	if slen <= 0 {
		return fmt.Errorf("ints len should not be 0")
	}

	var errInfo *bytes.Buffer

	for i := 0; i < slen; i++ {
		v := ints.GetVal(i, key)
		if v < min || max < v {
			if errInfo == nil {
				errInfo = &bytes.Buffer{}
			}
			errInfo.WriteString(fmt.Sprintf("\tkey:%s, val:%d is out of range [%d, %d]\n", key, v, min, max))
		}
	}

	if errInfo == nil {
		return nil
	} else {
		return fmt.Errorf(errInfo.String())
	}
}

func CheckIntListContain(ints IntKeyValList, key string, srcIntList IntList, srcName string) (err error) {
	slen := int(ints.GetLen())
	if slen <= 0 {
		return fmt.Errorf("ints len should not be 0")
	}

	var errInfo *bytes.Buffer

	for i := 0; i < slen; i++ {
		v := ints.GetVal(i, key)
		if !AIdsContainsBId(srcIntList, v) {
			if errInfo == nil {
				errInfo = &bytes.Buffer{}
			}
			errInfo.WriteString(fmt.Sprintf("\nkey:%s, row:%d, val:%d is not contained in %s\n", key, i, v, srcName))
		}
	}

	if errInfo == nil {
		return nil
	} else {
		return fmt.Errorf(errInfo.String())
	}
}

func CheckIntListContainAry(ints IntKeyValsList, key string, srcIntList IntList, srcName string) (err error) {
	slen := int(ints.GetLen())
	if slen <= 0 {
		return fmt.Errorf("ints len should not be 0")
	}

	var errInfo *bytes.Buffer

	for i := 0; i < slen; i++ {
		vals := ints.GetVals(i, key)
		for k := 0; k < len(vals); k++ {
			v := vals[k]
			if !AIdsContainsBId(srcIntList, v) {
				if errInfo == nil {
					errInfo = &bytes.Buffer{}
				}
				errInfo.WriteString(fmt.Sprintf("key:%s, row:%d, idx:%d, val:%d is not contained in %s\n", key, i, k, v, srcName))
			}
		}
	}

	if errInfo == nil {
		return nil
	} else {
		return fmt.Errorf(errInfo.String())
	}
}

func AStrsSetEqualToBStrsSet(aStrs []string, bStrs []string) (ok bool) {
	return len(StrsAContainStrsB(aStrs, bStrs)) == 0 && len(StrsAContainStrsB(bStrs, aStrs)) == 0
}

func AStrsCompleteEqualBStrs(aStrs []string, bStrs []string) (ok bool) {
	if len(aStrs) != len(bStrs) {
		return false
	}

	for k := 0; k < len(aStrs); k++ {
		if aStrs[k] != bStrs[k] {
			return false
		}
	}
	return true
}

func AIdsCompleteEqualBIds(aIds IntList, bIds IntList) (ok bool) {
	if aIds.GetLen() != bIds.GetLen() {
		return false
	}

	for k := 0; k < int(aIds.GetLen()); k++ {
		if aIds.GetInt(k) != bIds.GetInt(k) {
			return false
		}
	}
	return true
}

func AIdsCompleteEqualBIds2(aIds []int32, bIds []int32) (ok bool) {
	if len(aIds) != len(bIds) {
		return false
	}

	for k := 0; k < len(aIds); k++ {
		if aIds[k] != bIds[k] {
			return false
		}
	}
	return true
}
func Int64AIdsCompleteEqualBIds2(aIds []int64, bIds []int64) (ok bool) {
	if len(aIds) != len(bIds) {
		return false
	}

	for k := 0; k < len(aIds); k++ {
		if aIds[k] != bIds[k] {
			return false
		}
	}
	return true
}

func AIdsContainsBId(aIds IntList, bId int32) (ok bool) {
	for k := 0; k < int(aIds.GetLen()); k++ {
		if aIds.GetInt(k) == bId {
			return true
		}
	}
	return false
}
func AIdsContainsBId2(aIds []int32, bId int32) (ok bool) {
	for k := 0; k < len(aIds); k++ {
		if aIds[k] == bId {
			return true
		}
	}
	return false
}
func Int64AIdsContainsBId(aIds []int64, bId int64) (ok bool) {
	for k := 0; k < len(aIds); k++ {
		if aIds[k] == bId {
			return true
		}
	}
	return false
}

func AIdsContainsBIds(aIds IntList, bIds []int32) (bInvalidIds []int32, ok bool) {
	for i := 0; i < len(bIds); i++ {
		bId := bIds[i]
		found := false
		for k := 0; k < int(aIds.GetLen()); k++ {
			if aIds.GetInt(k) == bId {
				found = true
				break
			}
		}
		if !found {
			bInvalidIds = append(bInvalidIds, bId)
		}
	}

	return bInvalidIds, len(bInvalidIds) <= 0
}
func AIdsContainsBIds2(aIds []int32, bIds []int32) (bInvalidIds []int32, ok bool) {
	for i := 0; i < len(bIds); i++ {
		bId := bIds[i]
		found := false
		for k := 0; k < len(aIds); k++ {
			if aIds[k] == bId {
				found = true
				break
			}
		}
		if !found {
			bInvalidIds = append(bInvalidIds, bId)
		}
	}

	return bInvalidIds, len(bInvalidIds) <= 0
}

func Int64AIdsContainsBIds(aIds []int64, bIds []int64) (bInvalidIds []int64, ok bool) {
	for i := 0; i < len(bIds); i++ {
		bId := bIds[i]
		found := false
		for k := 0; k < len(aIds); k++ {
			if aIds[k] == bId {
				found = true
				break
			}
		}
		if !found {
			bInvalidIds = append(bInvalidIds, bId)
		}
	}

	return bInvalidIds, len(bInvalidIds) <= 0
}

func Int64AOverlapsInt64B(idsA []int64, idsB []int64) bool {
	for i := 0; i < len(idsB); i++ {
		idB := idsB[i]
		for k := 0; k < len(idsA); k++ {
			if idsA[k] == idB {
				return true
			}
		}
	}

	return false
}

func Int64AIdsSetEqualToBIdsSet(aIds []int64, bIds []int64) bool {
	_, ok1 := Int64AIdsContainsBIds(bIds, aIds)
	_, ok2 := Int64AIdsContainsBIds(aIds, bIds)
	return ok1 && ok2
}

func Int64AIdsCompareBIds(newIds []int64, oldIds []int64) (addIds []int64, modifyIds []int64, deleteIds []int64) {
	addIds, _ = Int64AIdsContainsBIds(oldIds, newIds)
	deleteIds, _ = Int64AIdsContainsBIds(newIds, oldIds)
	for i := 0; i < len(oldIds); i++ {
		bId := oldIds[i]
		for k := 0; k < len(newIds); k++ {
			if newIds[k] == bId {
				modifyIds = append(modifyIds, bId)
				break
			}
		}
	}

	return
}

func AIdsCompareBIds2(aIds []int32, bIds []int32) (addIds []int32, modifyIds []int32, deleteIds []int32) {
	addIds, _ = AIdsContainsBIds2(bIds, aIds)
	deleteIds, _ = AIdsContainsBIds2(aIds, bIds)
	for i := 0; i < len(bIds); i++ {
		bId := bIds[i]
		for k := 0; k < len(aIds); k++ {
			if aIds[k] == bId {
				modifyIds = append(modifyIds, bId)
				break
			}
		}
	}

	return
}

func StrsAContainStrsB(strsA []string, strsB []string) (bNotInA []string) {
	for i := 0; i < len(strsB); i++ {
		strB := strsB[i]
		found := false
		for k := 0; k < len(strsA); k++ {
			if strsA[k] == strB {
				found = true
				break
			}
		}
		if !found {
			bNotInA = append(bNotInA, strB)
		}
	}

	return bNotInA
}
func StrsAContainStrB(strsA []string, strB string) bool {
	for i := 0; i < len(strsA); i++ {
		if strsA[i] == strB {
			return true
		}
	}

	return false
}
func StrsAOverlapsStrsB(strsA []string, strsB []string) bool {
	for i := 0; i < len(strsB); i++ {
		strB := strsB[i]
		for k := 0; k < len(strsA); k++ {
			if strsA[k] == strB {
				return true
			}
		}
	}

	return false
}
func StrsACompareStrsB(strsNew []string, strsOld []string) (addStrs []string, modifyStrs []string, deleteStrs []string) {
	addStrs = StrsAContainStrsB(strsOld, strsNew)
	deleteStrs = StrsAContainStrsB(strsNew, strsOld)
	for i := 0; i < len(strsOld); i++ {
		u := strsOld[i]
		for k := 0; k < len(strsNew); k++ {
			if strsNew[k] == u {
				modifyStrs = append(modifyStrs, u)
				break
			}
		}
	}

	return
}

func CheckIntListNoIntersection(s0 IntList, s1 IntList) bool {
	for i := 0; i < int(s0.GetLen()); i++ {
		a := s0.GetInt(i)
		for k := 0; k < int(s1.GetLen()); k++ {
			if a == s1.GetInt(k) {
				return false
			}
		}
	}
	return true
}
func CheckInt32ListNoIntersection(s0 []int32, s1 []int32) bool {
	for i := 0; i < len(s0); i++ {
		a := s0[i]
		for k := 0; k < len(s1); k++ {
			if a == s1[k] {
				return false
			}
		}
	}
	return true
}
func CheckUint16ListNoIntersection(s0 []uint16, s1 []uint16) bool {
	for i := 0; i < len(s0); i++ {
		a := s0[i]
		for k := 0; k < len(s1); k++ {
			if a == s1[k] {
				return false
			}
		}
	}
	return true
}

func CheckLongListStrDuplicate(strs []string) (dupStr string, ok bool) {
	checker := map[string]int32{}
	dlen := len(strs)
	for i := 0; i < dlen; i++ {
		str := strs[i]
		_, existed := checker[str]
		if existed {
			return str, false
		} else {
			checker[str] = 1
		}
	}

	return "", true
}
func CheckShortListStrDuplicate(strs []string) (dupStr string, ok bool) {
	slen := len(strs)
	for i := 0; i < slen; i++ {
		str := strs[i]
		for k := 0; k < slen; k++ {
			if i != k && str == strs[k] {
				return str, false
			}
		}
	}

	return "", true
}

func CheckLongListDuplicateIdInt64(ids []int64) (dupId int64, ok bool) {
	checker := map[int64]int32{}
	dlen := len(ids)
	for i := 0; i < dlen; i++ {
		id := ids[i]
		_, existed := checker[id]
		if existed {
			return id, false
		} else {
			checker[id] = 1
		}
	}

	return 0, true
}
func CheckShortListDuplicateId(ids []int32) (dupId int32, ok bool) {
	dlen := len(ids)
	for i := 0; i < dlen; i++ {
		id := ids[i]
		for k := 0; k < dlen; k++ {
			if i != k && id == ids[k] {
				return id, false
			}
		}
	}

	return 0, true
}
func CheckShortListDuplicateIdInt64(ids []int64) (dupId int64, ok bool) {
	dlen := len(ids)
	for i := 0; i < dlen; i++ {
		id := ids[i]
		for k := 0; k < dlen; k++ {
			if i != k && id == ids[k] {
				return id, false
			}
		}
	}

	return 0, true
}

func CheckStringGroupDelta(s0 []string, s1 []string) (add []string, keep []string, del []string, err error) {
	dup, ok := CheckShortListStrDuplicate(s0)
	if !ok {
		err = fmt.Errorf("dup str(%s) in s0", dup)
		return
	}
	dup, ok = CheckShortListStrDuplicate(s1)
	if !ok {
		err = fmt.Errorf("dup str(%s) in s1", dup)
		return
	}

	checker := map[string]int8{}

	for i := 0; i < len(s0); i++ {
		checker[s0[i]] = -1
	}
	for i := 0; i < len(s1); i++ {
		checker[s1[i]]++
	}

	for str, cnt := range checker {
		switch cnt {
		case -1: // delete
			del = append(del, str)
		case 0: // keep
			keep = append(keep, str)
		case 1: // add
			add = append(add, str)
		}
	}
	return
}
